﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Xml;
using DocumentFormat.OpenXml.Packaging;

namespace OpenPowerPoint
{
    /// <summary>
    /// Represents the Properties of the Presentation file
    /// </summary>
    public class Properties : DependencyObject
    {
        /// <summary>
        /// The security level the presentation obtains
        /// </summary>
        public enum ESecurity
        {
            /// <summary>
            /// No security
            /// </summary>
            NoSecurity = 0,
            /// <summary>
            /// Password protected
            /// </summary>
            Password = 1,
            /// <summary>
            /// Recommended read only mode
            /// </summary>
            ReadOnlyRecommended = 2,
            /// <summary>
            /// Forced read only mode
            /// </summary>
            ReadOnlyForced = 4,
            /// <summary>
            /// locked presentation
            /// </summary>
            Locked = 8,
        }

        /// <summary>
        /// A struct describing the version information
        /// </summary>
        public struct SVersion
        {
            /// <summary>
            /// The major version number
            /// </summary>
            public int Major;
            /// <summary>
            /// The minor version number
            /// </summary>
            public int Minor;
        }
        #region Load/Save

        /// <summary>
        /// Loads the properties out of the according objects
        /// </summary>
        /// <param name="fileProperties">The CoreFilePropertiesPart for the main information</param>
        /// <param name="properties">The Properties for some unrelevant information</param>
        /// <returns>A new instance of properties for the presentation</returns>
        public static Properties Load(CoreFilePropertiesPart fileProperties, DocumentFormat.OpenXml.ExtendedProperties.Properties properties)
        {
            var ret = new Properties();

            ret.Characters = properties.Characters != null ? Convert.ToInt32(properties.Characters.Text) : 0;
            ret.CharactersInclSpaces = properties.CharactersWithSpaces != null
                                           ? Convert.ToInt32(properties.CharactersWithSpaces.Text)
                                           : 0;
            ret.Words = properties.Words != null ? Convert.ToInt32(properties.Words.Text) : 0;
            ret.Paragraphs = properties.Paragraphs != null ? Convert.ToInt32(properties.Paragraphs.Text) : 0;
            ret.Lines = properties.Lines != null ? Convert.ToInt32(properties.Lines.Text) : 0;

            ret.Slides = properties.Slides != null ? Convert.ToInt32(properties.Slides.Text) : 0;
            ret.HiddenSlides = properties.HiddenSlides != null ? Convert.ToInt32(properties.HiddenSlides.Text) : 0;
            ret.Notes = properties.Notes != null ? Convert.ToInt32(properties.Notes.Text) : 0;
            ret.Clips = properties.MultimediaClips != null ? Convert.ToInt32(properties.MultimediaClips.Text) : 0;

            ret.Application = properties.Application != null ? properties.Application.Text : "";
            ret.Version = properties.ApplicationVersion != null
                              ? new SVersion
                                    {
                                        Major = Convert.ToInt32(properties.ApplicationVersion.Text.Split('.')[0]),
                                        Minor =
                                            properties.ApplicationVersion.Text.Split('.').GetUpperBound(0) == 1         //Two items? (xx.yyy)
                                                ? Convert.ToInt32(properties.ApplicationVersion.Text.Split('.')[1])
                                                : 0
                                    }
                              : new SVersion { Major = 0, Minor = 0 };

            ret.Company = properties.Company != null ? properties.Company.Text : "";
            ret.DocumentSecurity = properties.DocumentSecurity != null
                                       ? (ESecurity)Convert.ToInt32(properties.DocumentSecurity.Text)
                                       : ESecurity.NoSecurity;
            ret.LinksUpToDate = properties.LinksUpToDate != null && properties.LinksUpToDate.Text != "false";
            ret.SharedDocument = properties.SharedDocument != null && properties.SharedDocument.Text != "false";
            ret.HyperlinkBase = properties.HyperlinkBase != null ? properties.HyperlinkBase.Text : "";
            ret.HyperlinksChanged = properties.HyperlinksChanged != null && properties.HyperlinksChanged.Text != "false";
            ret.PresentationFormat = properties.PresentationFormat != null ? properties.PresentationFormat.Text : "";
            ret.ScaleCrop = properties.ScaleCrop != null && properties.ScaleCrop.Text != "false";
            ret.SharedDocument = properties.SharedDocument != null && properties.SharedDocument.Text != "false";
            ret.TimeEdited = properties.TotalTime != null
                                 ? new TimeSpan(0, Convert.ToInt32(properties.TotalTime.Text), 0)
                                 : new TimeSpan(0, 0, 0);

            ret.LoadXml(new XmlTextReader(fileProperties.GetStream(FileMode.OpenOrCreate)));

            return ret;
        }

        /// <summary>
        /// Load remaining properties from xml
        /// </summary>
        /// <param name="reader">The xml reader to load the properties from</param>
        private void LoadXml(XmlTextReader reader)
        {
            while (reader.Read())
            {
                if (reader.Name.ToLower() == "cp:coreproperties")
                {
                    while (reader.Read())
                    {
                        if (reader.NodeType == XmlNodeType.EndElement &&
                            reader.Name.ToLower() == "cp:coreproperties") break;

                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            switch (reader.Name.ToLower())
                            {
                                case "dc:title":
                                    reader.Read();
                                    Title = reader.Value;
                                    break;
                                case "dc:subject":
                                    reader.Read();
                                    Subject = reader.Value;
                                    break;
                                case "dc:creator":
                                    reader.Read();
                                    Creators = new ObservableCollection<string>(reader.Value.Split(';'));
                                    break;
                                case "dc:description":
                                    reader.Read();
                                    Description = reader.Value;
                                    break;
                                case "cp:keywords":
                                    reader.Read();
                                    Keywords = new ObservableCollection<string>(reader.Value.Split(';'));
                                    break;
                                case "cp:lastmodifiedby":
                                    reader.Read();
                                    LastModifiedBy = reader.Value;
                                    break;
                                case "cp:revision":
                                    reader.Read();
                                    Revision = Convert.ToInt32(reader.Value);
                                    break;
                                case "cp:lastprinted":
                                    reader.Read();
                                    LastPrinted = DateTime.ParseExact(reader.Value, "yyyy-MM-ddTHH:mm:ssZ", null);
                                    break;
                                case "cp:category":
                                    reader.Read();
                                    Categories = new ObservableCollection<string>(reader.Value.Split(';'));
                                    break;
                                case "cp:contentstatus":
                                    reader.Read();
                                    ContentStatus = reader.Value;
                                    break;
                                case "dcterms:created":
                                    reader.Read();
                                    Created = DateTime.ParseExact(reader.Value, "yyyy-MM-ddTHH:mm:ssZ", null);
                                    break;
                                case "dcterms:modified":
                                    reader.Read();
                                    Modified = DateTime.ParseExact(reader.Value, "yyyy-MM-ddTHH:mm:ssZ", null);
                                    break;
                            }
                        }
                    }
                }
            }
        }

        #endregion Load/Save

        #region Constructors

        /// <summary>
        /// Initializes a new Instance
        /// </summary>
        public Properties()
        { }

        #endregion Constructors

        #region Properties
        public static readonly DependencyProperty ApplicationProperty =
            DependencyProperty.Register("Application", typeof(string), typeof(Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The application the presentation was created with
        /// </summary>
        public string Application
        {
            get { return (string)GetValue(ApplicationProperty); }
            set { SetValue(ApplicationProperty, value); }
        }

        public static readonly DependencyProperty CategoriesProperty =
            DependencyProperty.Register("Categories", typeof (ObservableCollection<string>), typeof (Properties), new PropertyMetadata(default(ObservableCollection<string>)));

        /// <summary>
        /// The categories assigned to this presentation
        /// </summary>
        public ObservableCollection<string> Categories
        {
            get { return (ObservableCollection<string>) GetValue(CategoriesProperty); }
            set { SetValue(CategoriesProperty, value); }
        }

        public static readonly DependencyProperty CharactersProperty =
            DependencyProperty.Register("Characters", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of characters used in this presentation
        /// </summary>
        public int Characters
        {
            get { return (int) GetValue(CharactersProperty); }
            set { SetValue(CharactersProperty, value); }
        }

        public static readonly DependencyProperty CharactersInclSpacesProperty =
            DependencyProperty.Register("CharactersInclSpaces", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of characters and spaces used in this presentation
        /// </summary>
        public int CharactersInclSpaces
        {
            get { return (int) GetValue(CharactersInclSpacesProperty); }
            set { SetValue(CharactersInclSpacesProperty, value); }
        }

        public static readonly DependencyProperty ClipsProperty =
            DependencyProperty.Register("Clips", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of multimedia clips used in this presentation
        /// </summary>
        public int Clips
        {
            get { return (int) GetValue(ClipsProperty); }
            set { SetValue(ClipsProperty, value); }
        }

        public static readonly DependencyProperty CompanyProperty =
            DependencyProperty.Register("Company", typeof (string), typeof (Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The company which made the presentation
        /// </summary>
        public string Company
        {
            get { return (string) GetValue(CompanyProperty); }
            set { SetValue(CompanyProperty, value); }
        }

        public static readonly DependencyProperty ContentStatusProperty =
            DependencyProperty.Register("ContentStatus", typeof (string), typeof (Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The status of the content
        /// </summary>
        public string ContentStatus
        {
            get { return (string) GetValue(ContentStatusProperty); }
            set { SetValue(ContentStatusProperty, value); }
        }

        public static readonly DependencyProperty CreatedProperty =
            DependencyProperty.Register("Created", typeof (DateTime), typeof (Properties), new PropertyMetadata(default(DateTime)));

        /// <summary>
        /// The date of creation
        /// </summary>
        public DateTime Created
        {
            get { return (DateTime) GetValue(CreatedProperty); }
            set { SetValue(CreatedProperty, value); }
        }

        public static readonly DependencyProperty CreatorsProperty =
            DependencyProperty.Register("Creators", typeof (ObservableCollection<string>), typeof (Properties), new PropertyMetadata(default(ObservableCollection<string>)));

        /// <summary>
        /// A list of initial creators
        /// </summary>
        public ObservableCollection<string> Creators
        {
            get { return (ObservableCollection<string>) GetValue(CreatorsProperty); }
            set { SetValue(CreatorsProperty, value); }
        }

        public static readonly DependencyProperty DescriptionProperty =
            DependencyProperty.Register("Description", typeof (string), typeof (Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The description of the presentation
        /// </summary>
        public string Description
        {
            get { return (string) GetValue(DescriptionProperty); }
            set { SetValue(DescriptionProperty, value); }
        }

        public static readonly DependencyProperty DocumentSecurityProperty =
            DependencyProperty.Register("DocumentSecurity", typeof (ESecurity), typeof (Properties), new PropertyMetadata(default(ESecurity)));

        /// <summary>
        /// The security status of this presentation
        /// </summary>
        public ESecurity DocumentSecurity
        {
            get { return (ESecurity) GetValue(DocumentSecurityProperty); }
            set { SetValue(DocumentSecurityProperty, value); }
        }

        public static readonly DependencyProperty HiddenSlidesProperty =
            DependencyProperty.Register("HiddenSlides", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of hidden slides contained in this document
        /// </summary>
        public int HiddenSlides
        {
            get { return (int) GetValue(HiddenSlidesProperty); }
            set { SetValue(HiddenSlidesProperty, value); }
        }

        public static readonly DependencyProperty HyperlinkBaseProperty =
            DependencyProperty.Register("HyperlinkBase", typeof (string), typeof (Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The base on which relative links build on
        /// </summary>
        public string HyperlinkBase
        {
            get { return (string) GetValue(HyperlinkBaseProperty); }
            set { SetValue(HyperlinkBaseProperty, value); }
        }

        public static readonly DependencyProperty HyperlinksChangedProperty =
            DependencyProperty.Register("HyperlinksChanged", typeof (bool), typeof (Properties), new PropertyMetadata(default(bool)));

        /// <summary>
        /// Whether the hyperlinks have changed or not
        /// </summary>
        public bool HyperlinksChanged
        {
            get { return (bool) GetValue(HyperlinksChangedProperty); }
            set { SetValue(HyperlinksChangedProperty, value); }
        }

        public static readonly DependencyProperty HyperlinksProperty =
            DependencyProperty.Register("Hyperlinks", typeof (ObservableCollection<string>), typeof (Properties), new PropertyMetadata(default(List<string>)));

        /// <summary>
        /// A list of all Hyperlinks used in this document
        /// </summary>
        public ObservableCollection<string> Hyperlinks
        {
            get { return (ObservableCollection<string>)GetValue(HyperlinksProperty); }
            set { SetValue(HyperlinksProperty, value); }
        }

        public static readonly DependencyProperty KeywordsProperty =
            DependencyProperty.Register("Keywords", typeof (ObservableCollection<string>), typeof (Properties), new PropertyMetadata(default(ObservableCollection<string>)));

        /// <summary>
        /// A list of keywords applied to this presentation
        /// </summary>
        public ObservableCollection<string> Keywords
        {
            get { return (ObservableCollection<string>) GetValue(KeywordsProperty); }
            set { SetValue(KeywordsProperty, value); }
        }

        public static readonly DependencyProperty LastModifiedByProperty =
            DependencyProperty.Register("LastModifiedBy", typeof (string), typeof (Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The name of the person who last modified it
        /// </summary>
        public string LastModifiedBy
        {
            get { return (string) GetValue(LastModifiedByProperty); }
            set { SetValue(LastModifiedByProperty, value); }
        }

        public static readonly DependencyProperty LastPrintedProperty =
            DependencyProperty.Register("LastPrinted", typeof (DateTime), typeof (Properties), new PropertyMetadata(default(DateTime)));

        /// <summary>
        /// The date when the presentation was last printed
        /// </summary>
        public DateTime LastPrinted
        {
            get { return (DateTime) GetValue(LastPrintedProperty); }
            set { SetValue(LastPrintedProperty, value); }
        }

        public static readonly DependencyProperty LinesProperty =
            DependencyProperty.Register("Lines", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of lines contained in this document
        /// </summary>
        public int Lines
        {
            get { return (int) GetValue(LinesProperty); }
            set { SetValue(LinesProperty, value); }
        }

        public static readonly DependencyProperty LinksUpToDateProperty =
            DependencyProperty.Register("LinksUpToDate", typeof (bool), typeof (Properties), new PropertyMetadata(default(bool)));

        /// <summary>
        /// Whether the links are up to date or not
        /// </summary>
        public bool LinksUpToDate
        {
            get { return (bool) GetValue(LinksUpToDateProperty); }
            set { SetValue(LinksUpToDateProperty, value); }
        }

        public static readonly DependencyProperty ModifiedProperty =
            DependencyProperty.Register("Modified", typeof (DateTime), typeof (Properties), new PropertyMetadata(default(DateTime)));

        /// <summary>
        /// The date of the last modification
        /// </summary>
        public DateTime Modified
        {
            get { return (DateTime) GetValue(ModifiedProperty); }
            set { SetValue(ModifiedProperty, value); }
        }

        public static readonly DependencyProperty NotesProperty =
            DependencyProperty.Register("Notes", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of notes contained in this presentation
        /// </summary>
        public int Notes
        {
            get { return (int) GetValue(NotesProperty); }
            set { SetValue(NotesProperty, value); }
        }

        public static readonly DependencyProperty ParagraphsProperty =
            DependencyProperty.Register("Paragraphs", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of paragraphs used in this presentation
        /// </summary>
        public int Paragraphs
        {
            get { return (int) GetValue(ParagraphsProperty); }
            set { SetValue(ParagraphsProperty, value); }
        }

        public static readonly DependencyProperty PresentationFormatProperty =
            DependencyProperty.Register("PresentationFormat", typeof (string), typeof (Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The size format label of the presentation
        /// </summary>
        public string PresentationFormat
        {
            get { return (string) GetValue(PresentationFormatProperty); }
            set { SetValue(PresentationFormatProperty, value); }
        }

        public static readonly DependencyProperty RevisionProperty =
            DependencyProperty.Register("Revision", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The revision number of the presentation
        /// </summary>
        public int Revision
        {
            get { return (int) GetValue(RevisionProperty); }
            set { SetValue(RevisionProperty, value); }
        }

        public static readonly DependencyProperty ScaleCropProperty =
            DependencyProperty.Register("ScaleCrop", typeof (bool), typeof (Properties), new PropertyMetadata(default(bool)));

        /// <summary>
        /// Whether to scale crops or not
        /// </summary>
        public bool ScaleCrop
        {
            get { return (bool) GetValue(ScaleCropProperty); }
            set { SetValue(ScaleCropProperty, value); }
        }

        public static readonly DependencyProperty SharedDocumentProperty =
            DependencyProperty.Register("SharedDocument", typeof (bool), typeof (Properties), new PropertyMetadata(default(bool)));

        /// <summary>
        /// Whether the presentation is a shared document
        /// </summary>
        public bool SharedDocument
        {
            get { return (bool) GetValue(SharedDocumentProperty); }
            set { SetValue(SharedDocumentProperty, value); }
        }

        public static readonly DependencyProperty SlidesProperty =
            DependencyProperty.Register("Slides", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of slides contained in this document
        /// </summary>
        public int Slides
        {
            get { return (int) GetValue(SlidesProperty); }
            set { SetValue(SlidesProperty, value); }
        }

        public static readonly DependencyProperty SubjectProperty =
            DependencyProperty.Register("Subject", typeof (string), typeof (Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The subject of the presentation
        /// </summary>
        public string Subject
        {
            get { return (string) GetValue(SubjectProperty); }
            set { SetValue(SubjectProperty, value); }
        }

        public static readonly DependencyProperty TimeEditedProperty =
            DependencyProperty.Register("TimeEdited", typeof (TimeSpan), typeof (Properties), new PropertyMetadata(default(TimeSpan)));

        /// <summary>
        /// The total amount of time the document was edited
        /// </summary>
        public TimeSpan TimeEdited
        {
            get { return (TimeSpan) GetValue(TimeEditedProperty); }
            set { SetValue(TimeEditedProperty, value); }
        }

        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof (string), typeof (Properties), new PropertyMetadata(default(string)));

        /// <summary>
        /// The title of the presentation
        /// </summary>
        public string Title
        {
            get { return (string) GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        public static readonly DependencyProperty VersionProperty =
            DependencyProperty.Register("Version", typeof (SVersion), typeof (Properties), new PropertyMetadata(default(SVersion)));

        /// <summary>
        /// The version number of the application the presentation was created with
        /// </summary>
        public SVersion Version
        {
            get { return (SVersion) GetValue(VersionProperty); }
            set { SetValue(VersionProperty, value); }
        }

        public static readonly DependencyProperty WordsProperty =
            DependencyProperty.Register("Words", typeof (int), typeof (Properties), new PropertyMetadata(default(int)));

        /// <summary>
        /// The number of words contained in this presentation
        /// </summary>
        public int Words
        {
            get { return (int) GetValue(WordsProperty); }
            set { SetValue(WordsProperty, value); }
        }

        #endregion Properties
    }
}